package sfeir.authform.server.authservice.standard;

import java.util.Properties;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpSession;

import sfeir.authform.client.dao.AuthUser;
import sfeir.authform.server.AuthServlet;
import sfeir.authform.server.authservice.standard.entity.UserEntity;

public class AuthStandard {
    private static final PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional");
    private HttpSession session = null;
    private String from, subject, bodyBefore, bodyAfter;

    public AuthStandard(HttpSession session) {
        super();
        this.session = session;
    }

    public Boolean deleteUser(AuthUser user) {
        PersistenceManager pm = pmfInstance.getPersistenceManager();
        try {
            UserEntity entity = pm.getObjectById(UserEntity.class, user.getId());
            pm.deletePersistent(entity);
            if (session != null) {
                AuthUser u = (AuthUser) session.getAttribute("__authService.usersession");
                if (u != null && u.getId() == user.getId())
                    session.invalidate();
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pm.close();
        }
        return false;
    }

    /**
     * Define here the parameters for the sending of email to your users. This
     * will be used for the retrieving of passwords.
     * 
     * @param from
     *            The email address of one of the developpers of your AppEngine
     *            application.<br/>
     *            Most of the time an address is created with the domain name,
     *            and added to the developpers list in the appengine
     *            administration panel. <br/>
     *            Thus to be used just for communication with users.
     * @param subject The subject of the mail.
     * @param bodyBefore The whole part of the body to be written before the password.
     * @param bodyAfter The part of the body to be written after the password.
     */
    public void setMailPasswordSettings(String from, String subject, String bodyBefore, String bodyAfter) {
        this.from = from;
        this.subject = subject;
        this.bodyBefore = bodyBefore;
        this.bodyAfter = bodyAfter;
    }

    /**
     * Send the mail to the given user, with new password.<br/>
     * ENSURE you have set the mail setting throught the
     * <code>setMailPasswordSettings(String from, String subject, String bodyBefore, String bodyAfter)</code>
     * method before sending any mail.
     * 
     * @param user
     * @param password
     */
    private void sendMailPassword(AuthUser user, String password) {
        if (from != null && subject != null && bodyBefore != null && bodyAfter != null) {
            Properties props = new Properties();
            Session session = Session.getDefaultInstance(props, null);

            try {
                Message msg = new MimeMessage(session);
                msg.setFrom(new InternetAddress(from));
                msg.addRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
                msg.setSubject(subject);
                msg.setText(bodyBefore + password + bodyAfter);
                Transport.send(msg);

            } catch (AddressException e) {
                e.printStackTrace();
            } catch (MessagingException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Set a new password for the given user, identified by its login.
     * 
     * @param login
     *            the users's login that ask for a new password
     * @return Whither the password retrieving process has been achieved
     *         succefully or not
     */
    public Boolean getLostPassword(String login) {

        if (login == null)
            return false;
        PersistenceManager pm = pmfInstance.getPersistenceManager();
        try {
            Query query = pm.newQuery(UserEntity.class);
            query.setFilter("email == Login");
            query.declareParameters("String Login");
            query.setUnique(true);
            UserEntity entity = (UserEntity) query.execute(login);
            if (entity == null)
                return false;
            AuthUser user = entity.toUser();
            // Create a random password
            String newPass = getRandomPassword(8);
            String pass = AuthServlet.md5(newPass);
            user.setPassword(pass);
            updateUser(user);
            // send the mail
            sendMailPassword(user, newPass);
            return true;

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pm.close();
        }
        return false;
    }

    /**
     * Generated a random alphanumerical password [A-Za-z0-9]
     * 
     * @param length
     *            The number of charaters wanted for the new password
     * @return The new password
     */
    private String getRandomPassword(int length) {
        StringBuffer sb = new StringBuffer();
        // 65-90, 97-122, 48-57
        for (int x = 0; x < length; x++) {
            char min = (char) ((int) (Math.random() * 26) + 97);
            char maj = (char) ((int) (Math.random() * 26) + 65);
            char nb = (char) ((int) (Math.random() * 9) + 48);
            int r = ((int) (Math.random() * 3));
            switch (r) {
            case 0:
                sb.append(min);
                break;
            case 1:
                sb.append(maj);
                break;
            case 2:
                sb.append(nb);
                break;
            }
        }
        return sb.toString();
    }

    public AuthUser login(String login, String password) {
        if (login == null || password == null)
            return null;
        PersistenceManager pm = pmfInstance.getPersistenceManager();
        try {
            Query query = pm.newQuery(UserEntity.class);
            query.setFilter("email == Login");
            query.declareParameters("String Login");
            query.setUnique(true);
            UserEntity entity = (UserEntity) query.execute(login);
            if (entity == null)
                return null;
            AuthUser user = entity.toUser();
            if (!password.equals(entity.getPassword()))
                return null;
            if (session != null) {
                session.setAttribute("__authService.usersession", user);
            }
            return user;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pm.close();
        }
        return null;
    }

    public void logout() {
        if (session != null) {
            session.invalidate();
            session = null;
        }
    }

    public Boolean signup(AuthUser user) {
        UserEntity userCheck = getUser(user.getEmail());
        if (userCheck != null)
            return false;
        PersistenceManager pm = pmfInstance.getPersistenceManager();
        try {
            UserEntity entity = new UserEntity(user);
            pm.makePersistent(entity);
            if (session != null) {
                session.setAttribute("__authService.usersession", userCheck);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pm.close();
        }
        return false;
    }

    public Boolean updateUser(AuthUser user) {
        PersistenceManager pm = pmfInstance.getPersistenceManager();
        try {
            UserEntity entity = pm.getObjectById(UserEntity.class, user.getId());
            entity.fromUser(user);
            pm.makePersistent(entity);
            if (session != null) {
                AuthUser u = (AuthUser) session.getAttribute("__authService.usersession");
                if (u != null && u.getId() == user.getId())
                    session.setAttribute("__authService.usersession", user);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pm.close();
        }
        return false;
    }

    public UserEntity getUser(long id) {
        PersistenceManager pm = pmfInstance.getPersistenceManager();
        UserEntity user = null;
        try {
            user = pm.getObjectById(UserEntity.class, id);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pm.close();
        }
        return user;
    }

    public UserEntity getUser(String email) {
        PersistenceManager pm = pmfInstance.getPersistenceManager();
        UserEntity user = null;
        try {
            Query query = pm.newQuery(UserEntity.class);
            query.setFilter("email == Login");
            query.declareParameters("String Login");
            query.setUnique(true);
            user = (UserEntity) query.execute(email);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pm.close();
        }
        return user;
    }

    public void setSession(HttpSession session) {
        this.session = session;
    }
}
